// https://learn.microsoft.com/en-us/typography/opentype/spec/gdef

use super::super::ggg::*;
use super::super::var::*;
use super::*;

#[derive(Debug, Clone)]
pub struct Table<'a> {
	pub major_version: u16, // Major version of the GPOS table, = 1.
	pub minor_version: u16, // Minor version of the GPOS table, = 0.
	// pub glyph_class_def_offset: Option<Offset16>, // Offset to class definition table for glyph type, from beginning of GDEF header (may be NULL).
	pub glyph_classs: Option<ClassDefinition<'a>>,
	// pub attach_list_offset: Option<Offset16>, // Offset to attachment point list table, from beginning of GDEF header (may be NULL).
	pub attach_lists: Option<AttachList<'a>>,
	// pub lig_caret_list_offset: Option<Offset16>, // Offset to ligature caret list table, from beginning of GDEF header (may be NULL).
	pub lig_caret_lists: Option<LigCaretList<'a>>,
	// pub mark_attach_class_def_offset: Option<Offset16>, // Offset to class definition table for mark attachment type, from beginning of GDEF header (may be NULL).}
	pub mark_attach_classs: Option<ClassDefinition<'a>>,
	// version 1.2
	// pub mark_glyph_sets_def_offset: Option<Offset16>, // Offset to the table of mark glyph set definitions, from beginning of GDEF header (may be NULL).
	pub mark_glyph_sets: Option<MarkGlyphSets<'a>>,
	// version 1.3
	// Offset to the item variation store table, from beginning of GDEF header (may be NULL).
	pub item_var_store_offset: Option<ItemVariationStore<'a>>,
}
impl<'a> Table<'a> {
	pub fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let major_version = s.read()?;
		let minor_version = s.read()?;
		let this = Table {
			major_version,
			minor_version,
			glyph_classs: s.parse_at_offset16(data),
			attach_lists: s.parse_at_offset16(data),
			lig_caret_lists: s.parse_at_offset16(data),
			mark_attach_classs: s.parse_at_offset16(data),
			mark_glyph_sets: if minor_version >= 2 { s.parse_at_offset16(data) } else { None },
			item_var_store_offset: if minor_version >= 3 {
				s.read_at_offset32(data).map(Stream::new).and_then(ItemVariationStore::parse)
			} else {
				None
			},
		};
		Some(this)
	}
	/// Checks that face has
	/// [Glyph Class Definition Table](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table).
	#[inline]
	pub fn has_glyph_classes(&self) -> bool {
		self.glyph_classs.is_some()
	}

	/// Returns glyph's class according to
	/// [Glyph Class Definition Table](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table).
	///
	/// Returns `None` when *Glyph Class Definition Table* is not set
	/// or glyph class is not set or invalid.
	#[inline]
	pub fn glyph_class(&self, glyph_id: GlyphId) -> Option<GlyphClassDef> {
		match self.glyph_classs?.get(glyph_id) {
			1 => Some(GlyphClassDef::Base),
			2 => Some(GlyphClassDef::Ligature),
			3 => Some(GlyphClassDef::Mark),
			4 => Some(GlyphClassDef::Component),
			_ => None,
		}
	}
	/// Returns glyph's mark attachment class according to
	/// [Mark Attachment Class Definition Table](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gdef#mark-attachment-class-definition-table).
	///
	/// All glyphs not assigned to a class fall into Class 0.
	#[inline]
	pub fn mark_attach_class(&self, glyph_id: GlyphId) -> u16 {
		self.mark_attach_classs.map(|def| def.get(glyph_id)).unwrap_or(0)
	}
}

// https://learn.microsoft.com/en-us/typography/opentype/spec/gdef#glyph-class-definition-table
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub enum GlyphClassDef {
	Base = 1,      // Base glyph (single character, spacing glyph)
	Ligature = 2,  // Ligature glyph (multiple character, spacing glyph)
	Mark = 3,      // Mark glyph (non-spacing combining glyph)
	Component = 4, // Component glyph (part of single character, spacing glyph)
}
impl PartialEq<u16> for GlyphClassDef {
	fn eq(&self, other: &u16) -> bool {
		match (self, other) {
			(Self::Base, 1) | (Self::Ligature, 2) | (Self::Mark, 3) | (Self::Component, 4) => true,
			_ => false,
		}
	}
}

#[derive(Debug, Clone)]
pub struct AttachList<'a> {
	// pub coverage_offset: Offset16, // Offset to Coverage table, from beginning of AttachList table.
	pub coverage: Coverage<'a>,
	// pub glyph_count: u16, // Number of glyphs with attachment points.
	// pub attach_point_offsets: LazyArray16<'a, Offset16>, // Array of offsets to AttachPoint tables, from beginning of AttachList table, in Coverage Index order.
	pub attach_points: LazyOffsetArray16<'a, AttachPoint<'a>>,
}
impl<'a> FromSlice<'a> for AttachList<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			coverage: s.parse_at_offset16(data)?,
			attach_points: LazyOffsetArray16::new(data, s.parse_array16()?),
		})
	}
}

pub type AttachPoint<'a> = LazyArray16<'a, u16>;

#[derive(Debug, Clone)]
pub struct LigCaretList<'a> {
	// Offset16	coverageOffset	Offset to Coverage table, from beginning of LigCaretList table.
	pub coverage: Coverage<'a>,
	// uint16	ligGlyphCount	Number of ligature glyphs.
	// Offset16	ligGlyphOffsets[ligGlyphCount]	Array of offsets to LigGlyph tables, from beginning of LigCaretList table, in Coverage index order.
	pub lig_glyphs: LazyOffsetArray16<'a, LigGlyph<'a>>,
}
impl<'a> FromSlice<'a> for LigCaretList<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			coverage: s.parse_at_offset16(data)?,
			lig_glyphs: LazyOffsetArray16::new(data, s.parse_array16()?),
		})
	}
}

// uint16	caretCount	Number of caret value tables for this ligature (components - 1).
// Offset16	caretValueOffsets[caretCount]	Array of offsets to caret value tables, from beginning of LigGlyph table, in increasing coordinate order.
pub type LigGlyph<'a> = LazyOffsetArray16<'a, CaretValue>;

#[derive(Debug, Clone, Copy)]
pub enum CaretValue {
	Format1 {
		// uint16	format	Format identifier — format = 1.
		coordinate: i16, // X or Y value, in design units.
	},
	Format2 {
		// uint16	format	Format identifier — format = 2.
		caret_value_point_index: u16, // Contour point index on glyph.
	},
}
impl<'a> FromSlice<'a> for CaretValue {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let format: u16 = s.read()?;
		Some(match format {
			1 => Self::Format1 { coordinate: s.read()? },
			2 => Self::Format2 {
				caret_value_point_index: s.read()?,
			},
			_ => return None,
		})
	}
}

#[derive(Clone)]
pub struct MarkGlyphSets<'a> {
	pub format: u16, // Format identifier — format = 1.
	// pub mark_glyph_set_count: u16,                   // Number of mark glyph sets defined.
	pub coverage_offsets: LazyArray16<'a, Offset32>, // Array of offsets to mark glyph set coverage tables, from the start of the MarkGlyphSets table.

	pub data: &'a [u8],
}
impl<'a> FromSlice<'a> for MarkGlyphSets<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			format: s.read()?,
			coverage_offsets: s.parse_array16()?,
			data,
		})
	}
}
impl<'a> MarkGlyphSets<'a> {
	pub fn get_coverage(&self, index: u16) -> Option<Coverage<'a>> {
		self.get_coverage_(self.coverage_offsets.get(index)?)
	}
	pub fn get_coverage_(&self, r: Offset32) -> Option<Coverage<'a>> {
		Coverage::parse(self.data.get(r.to_usize()..)?)
	}
}
impl<'a> core::fmt::Debug for MarkGlyphSets<'a> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_list()
			.entries(
				self.coverage_offsets
					.into_iter()
					.map(|v| self.data.get(v.to_usize()..))
					.flatten()
					.map(Coverage::parse)
					.flatten(),
			)
			.finish()
	}
}
